import { Button, Radio } from 'antd';
import { fabric } from 'fabric';
import React, { useEffect, useRef, useState } from 'react';

import Empty from '@/components/common/Empty';
import { getBusinessOptions } from '@/utils';

import {
  addCroppedLayers,
  findSubArrayIndex,
  getImageCroppedInfo,
  getImageScale
} from '../utils';
import styles from './index.module.less';
import { ScreenProps } from './interface';

// 画布可见大小
const visualCanvasSize = 400;
// 画布原本大小
const originCanvasSize = 1000;
// 画布缩放比例
const canvasScale = visualCanvasSize / originCanvasSize;

const Screen = ({
  toBeCroppedImage,
  saveCroppedImage,
  isRatioLocked
}: ScreenProps): JSX.Element => {
  // 当前裁剪图片索引
  const [size, setSize] = useState<[number, number]>([1000, 1000]);
  const [scaleIndex, setScaleInfo] = useState(0);
  const fabricRef = useRef<fabric.Canvas>(null);

  const { productSize } = getBusinessOptions();
  const { list = [] } = productSize;
  const scaleList = list.map((item) =>
    ((item.value as string) || '').split(':').map(Number)
  );

  // 更改比例索引
  const onChange = (e): void => {
    setScaleInfo(e.target.value);
  };

  // 裁剪函数
  const handleCrop = async (): Promise<void> => {
    const croppedInfo = await getImageCroppedInfo(
      fabricRef.current,
      originCanvasSize
    );
    saveCroppedImage(
      croppedInfo.croppedRadioInfo,
      croppedInfo.imgSrc,
      croppedInfo.width,
      croppedInfo.height,
      scaleList[scaleIndex] as [number, number]
    );
  };

  useEffect(() => {
    let tmpScaleIndex = 0;

    // 没有图片或者图片没有设置裁剪比例，则默认为第一项
    if (!toBeCroppedImage || !toBeCroppedImage.ratio) {
      tmpScaleIndex = 0;
    } else {
      tmpScaleIndex = findSubArrayIndex(toBeCroppedImage.ratio, scaleList);
    }

    // 设置Scale
    setScaleInfo(tmpScaleIndex);

    // 创建 Fabric.js 画布实例
    if (!fabricRef.current) {
      fabricRef.current = new fabric.Canvas('cropScreen', {
        backgroundColor: '#fff',
        preserveObjectStacking: true,
        width: visualCanvasSize,
        height: visualCanvasSize,
        selection: false
      });
      // 原始画布为1000 * 1000，通过缩放，将画布可见大小变为400 * 400（画布实际还是按照 1000 * 1000放入图层）
      fabricRef.current.setZoom(canvasScale);
    }
  }, [toBeCroppedImage]);

  useEffect(() => {
    // 画布未初始化，则返回
    if (!fabricRef.current || !toBeCroppedImage) {
      return;
    }

    const productRadio = {
      width: scaleList[scaleIndex][0],
      height: scaleList[scaleIndex][1]
    };

    // 获取铺满画布的图层大小以及图片铺满画布的缩放比例
    const imageScale = getImageScale(
      toBeCroppedImage.originWidth,
      toBeCroppedImage.originHeight,
      originCanvasSize
    );

    // 加载待裁剪图片
    fabric.Image.fromURL(toBeCroppedImage.originSrc, (imageLayer) => {
      // 所有在加载图片完成的异步操作与 useEffect 并不同步，清空画布需要放在加载图片完成之后
      fabricRef.current.clear();

      imageLayer.set({
        top: originCanvasSize / 2,
        left: originCanvasSize / 2,
        // 图层的长宽，图片默认会铺满图层
        width: toBeCroppedImage.originWidth,
        height: toBeCroppedImage.originHeight,
        // 缩放图层，保证长边占满 1000 * 1000 的画布
        scaleX: imageScale,
        scaleY: imageScale,
        opacity: 1,
        selectable: false, // 不可选中
        lockMovementX: true, // 锁定水平移动
        lockMovementY: true, // 锁定垂直移动
        lockRotation: true, // 锁定旋转
        data: {
          isBeCropped: true
        }
      });

      fabricRef.current.add(imageLayer);

      // 显示裁剪框大小
      function showCropInfo(width: number, height: number): void {
        // 向上取整
        width = Math.min(Math.ceil(width), originCanvasSize);
        height = Math.min(Math.ceil(height), originCanvasSize);
        setSize([width, height]);
      }

      // 进入裁剪模式
      addCroppedLayers(
        fabricRef.current,
        imageLayer,
        originCanvasSize,
        productRadio,
        showCropInfo
      );
    });
  }, [scaleIndex, toBeCroppedImage]);

  return (
    <div>
      <div className={styles['info-container']}>
        <ul className={styles.info}>
          <li className={styles['info-name']}>Scale:</li>
          <li className={styles['info-value']}>
            <Radio.Group
              onChange={onChange}
              value={scaleIndex}
              style={{ lineHeight: '32px' }}
              disabled={isRatioLocked}
            >
              {scaleList.map((scale, index) => {
                return (
                  <Radio
                    key={index}
                    value={index}
                  >{`${scale[0]}x${scale[1]}`}</Radio>
                );
              })}
            </Radio.Group>
          </li>
        </ul>
        <ul className={styles.info}>
          <li className={styles['info-name']}>Size:</li>
          <li className={styles['info-value']}>
            {`${size[0]} `}
            <span className={styles['info-symbol']}>X</span>
            {` ${size[1]}`}
          </li>
        </ul>
      </div>
      <div
        className={styles['canvas-container']}
        style={{ width: visualCanvasSize + 2, height: visualCanvasSize + 2 }}
      >
        <canvas
          id="cropScreen"
          width={visualCanvasSize}
          height={visualCanvasSize}
        ></canvas>
        {!toBeCroppedImage ? (
          <div className={styles['empty-container']}>
            <Empty className={'m-auto'} emptyText="No Image" />
          </div>
        ) : null}
      </div>
      <div className={styles['btn-container']}>
        <Button
          type="primary"
          block
          onClick={handleCrop}
          disabled={!toBeCroppedImage}
        >
          Crop
        </Button>
      </div>
    </div>
  );
};

export default Screen;
